package com.chatsimple.socket

import com.chatsimple.Const
import com.chatsimple.logDebug
import com.ly.data.MessageProtobuf
import kotlinx.coroutines.*
import java.io.BufferedInputStream
import java.io.BufferedOutputStream
import java.net.ServerSocket
import java.net.Socket
import java.nio.ByteBuffer
import java.nio.ByteOrder

/**
 * 直接用ServerSocket创建的简易服务端
 */
@Suppress("unused")
class SimpleSocketServer {

    private var cacheBytes = byteArrayOf()

    fun start() {
        try {
            val serverSocket = ServerSocket(Const.PORT)
            val coroutine = CoroutineScope(Dispatchers.IO + SupervisorJob())
            logDebug("Server Started on port 6666")
            while (true) {
                val client = serverSocket.accept()
                logDebug("Client connect from :${client.remoteSocketAddress}")
                coroutine.launch {
                    handleClient(client)
                }
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private suspend fun handleClient(client: Socket) = withContext(Dispatchers.IO) {
        try {
            logDebug("handleClient")
            val inputStream = client.getInputStream()
            val outputStream = client.getOutputStream()
            val bufferIs = BufferedInputStream(inputStream)
            val bufferOs = BufferedOutputStream(outputStream)
            val receivedBytes = ByteArray(4096)
            while (!client.isClosed) {
                val receivedLength = bufferIs.read(receivedBytes, 0, receivedBytes.size)
                if (receivedLength > 0) {
                    handlePackage(receivedLength, receivedBytes, bufferOs)
                }
            }
        } catch (e: Exception) {
            client.close()
        }
    }


    private fun handlePackage(
        receivedLength: Int,
        receivedBytes: ByteArray,
        bufferOs: BufferedOutputStream
    ) {
        //假设上一次的数据解析有剩余，需要将剩余的bytes合并到receivedBytes中，并且剩余的要在头部位置
        var mergeArray = if (receivedLength < receivedBytes.size) {
            mergeArray(cacheBytes, receivedBytes.copyOfRange(0, receivedLength))
        } else {
            mergeArray(cacheBytes, receivedBytes)
        }
        //包头不足Int.SIZE
        if (mergeArray.size < Int.SIZE_BYTES) {
            cacheBytes = mergeArray
            return
        }
        //取出包头长度
        val lengthBytes = mergeArray.copyOfRange(0, Int.SIZE_BYTES)
        val buf = ByteBuffer.wrap(lengthBytes).order(ByteOrder.LITTLE_ENDIAN)
        var contentLength = buf.getInt()
        logDebug("Received contentLength,${contentLength}")
        var packageLength = contentLength + Int.SIZE_BYTES
        while (mergeArray.size > packageLength) {
            val body = mergeArray.copyOfRange(Int.SIZE_BYTES, packageLength)
            handleContent(bufferOs, body)
            //剩余的数据
            mergeArray = mergeArray.copyOfRange(packageLength, mergeArray.size)
            //如果剩余的长度大于一个包头 处理粘包数据
            if (mergeArray.size >= Int.SIZE_BYTES) {
                contentLength =
                    ByteBuffer.wrap(mergeArray.copyOfRange(0, Int.SIZE_BYTES)).order(ByteOrder.LITTLE_ENDIAN)
                        .getInt()
                //下一个包的长度就是
                packageLength = contentLength + Int.SIZE_BYTES
            } else {
                //不足一个包头，可能是其他包的剩余数据,再从缓冲区读
                cacheBytes = mergeArray
                return
            }
        }
        //如果剩余的数组刚好是一个完整的包长度，那就取出指定缓存
        cacheBytes = if (mergeArray.size == packageLength) {
            logDebug("Received 剩余数组刚好是一个包长度")
            handleContent(bufferOs, mergeArray.copyOfRange(Int.SIZE_BYTES, packageLength))
            byteArrayOf()
        } else {
            mergeArray
        }
    }


    private fun handleContent(outputStream: BufferedOutputStream, content: ByteArray) {
        val task = MessageProtobuf.TaskProto.parseFrom(content).toBuilder()
            .setType(MessageProtobuf.TaskType.Response)
            .build()
        logDebug("handleContent content:$task")
        val byteBuffer = ByteBuffer.allocate(Int.SIZE_BYTES + content.size)
        byteBuffer.order(ByteOrder.LITTLE_ENDIAN)
            .putInt(content.size)
            .put(task.toByteArray())
            .flip()
        val byteArray = ByteArray(Int.SIZE_BYTES + content.size)
        byteBuffer.get(byteArray, 0, Int.SIZE_BYTES + content.size)
        //直接转发回给客户端
        outputStream.write(byteArray, 0, byteArray.size)
        outputStream.flush()
    }

    private fun mergeArray(bytesOne: ByteArray, bytesTwo: ByteArray): ByteArray {
        val mergeBytes = ByteArray(bytesOne.size + bytesTwo.size)
        System.arraycopy(bytesOne, 0, mergeBytes, 0, bytesOne.size)
        System.arraycopy(bytesTwo, 0, mergeBytes, bytesOne.size, bytesTwo.size)
        return mergeBytes
    }

}